/*
*    Qizx/open 3.0 beta
*
* This code is the open-source version of Qizx.
* Copyright (C) 2004-2009 Axyana Software -- All rights reserved.
*
* The contents of this file are subject to the Mozilla Public License 
*  Version 1.1 (the "License"); you may not use this file except in 
*  compliance with the License. You may obtain a copy of the License at
*  http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
*  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
*  for the specific language governing rights and limitations under the
*  License.
*
* The Initial Developer of the Original Code is Xavier Franc - Axyana Software.
*
*/
using System;
using EvaluationException = System.Qizx.Api.EvaluationException;
using FullTextFactory = System.Qizx.Api.FullText.FullTextFactory;
using TextTokenizer = System.Qizx.Api.FullText.TextTokenizer;
using FullText = System.Qizx.Queries.FullText;
using MatchOptions = System.Qizx.Queries.MatchOptions;
using Selection = System.Qizx.Queries.Selection;
using SelectionList = System.Qizx.Queries.SelectionList;
using SimpleWord = System.Qizx.Queries.SimpleWord;
using Wildcard = System.Qizx.Queries.Wildcard;
using EvalContext = System.Qizx.XQuery.EvalContext;
using Focus = System.Qizx.XQuery.Focus;
using ModuleContext = System.Qizx.XQuery.ModuleContext;
using XQValue = System.Qizx.XQuery.XQValue;
using StringArraySequence = System.Qizx.XQuery.Dt.StringArraySequence;
namespace System.Qizx.XQuery.Op
{
	
	
	/// <summary> Primary FT Selection: evaluates to a list of word sequences 
	/// with any/all option and occurrence option.
	/// </summary>
	public class FTWordsOp:FTSelectionOp
	{
		public override bool Constant
		{
			get
			{
				return words.Constant && (occs == null || occs.Constant);
			}
			
		}
		public int anyAll = System.Qizx.Queries.FullText_Fields.ANY;
		public RangeExpr occs;
		public Expression words;
		
         public FTWordsOp(FTSelectionOp exp1, FTSelectionOp exp2)
            : base(exp1) {
            this.addChild(exp2);
        }

         public FTWordsOp(Expression exp1)
            : base(exp1) {
        }
		
		public override Expression StaticCheck(ModuleContext context, int flags)
		{
			base.StaticCheck(context, flags);
			words = context.staticCheck(words, flags);
			occs = (RangeExpr) context.staticCheck(occs, flags);
			return this;
		}
		
		public override Selection expand(Focus focus, EvalContext context, MatchOptions inherited, float inheritedWeight)
		{
			SelectionList result = null;
			
			// compute words: the expression may return a sequence of strings
			XQValue res = words.eval(focus, context);
			System.String[] seq = StringArraySequence.expand(res);
			
			// setup word tokenizer from options: case, diacritics, stemming etc
			MatchOptions mops = expandMatchOptions(inherited);
			float weight = expandWeight(focus, context, inheritedWeight);
			ModuleContext sctx = context.StaticContext;
			FullTextFactory ftp = sctx.FulltextFactory;
			
			TextTokenizer tokenizer = ftp.getTokenizer(mops.language);
			tokenizer.AcceptingWildcards = mops.wildcards == MatchOptions.WITH;
			tokenizer.ParsingSpecialChars = false;
			
			switch (anyAll)
			{
				
				case System.Qizx.Queries.FullText_Fields.ALL_WORDS:  // single word list
					result = new System.Qizx.Queries.And();
					for (int i = 0; i < seq.Length; i++)
						expandTerms(seq[i], result, tokenizer, mops, weight);
					break;
				
				case System.Qizx.Queries.FullText_Fields.ANY_WORD:  // single word list
					result = new System.Qizx.Queries.Or();
					result.Weight = weight;
					for (int i = 0; i < seq.Length; i++)
						expandTerms(seq[i], result, tokenizer, mops, weight);
					break;
				
				case System.Qizx.Queries.FullText_Fields.ALL:  // list of phrases
					if (seq.Length <= 1)
					{
						result = new System.Qizx.Queries.Phrase();
						expandTerms(seq[0], result, tokenizer, mops, weight);
					}
					else
					{
						result = new System.Qizx.Queries.And();
						for (int i = 0; i < seq.Length; i++)
							result.addChild(expandTerms(seq[i], new System.Qizx.Queries.Phrase(), tokenizer, mops, weight));
					}
					break;
				
				case System.Qizx.Queries.FullText_Fields.ANY: 
					if (seq.Length <= 1)
					{
						result = new System.Qizx.Queries.Phrase();
						expandTerms(seq[0], result, tokenizer, mops, weight);
					}
					else
					{
						result = new System.Qizx.Queries.Or();
						for (int i = 0; i < seq.Length; i++)
							result.addChild(expandTerms(seq[i], new System.Qizx.Queries.Phrase(), tokenizer, mops, weight));
					}
					break;
				
				case System.Qizx.Queries.FullText_Fields.PHRASE: 
					result = new System.Qizx.Queries.Phrase();
					for (int i = 0; i < seq.Length; i++)
						expandTerms(seq[i], result, tokenizer, mops, weight);
					
					break;
				}
			
			// optional 'occurs' range:
			if (occs != null)
			{
				result.occRange = occs.evaluate(focus, context);
			}
			
			result.MatchOptions = mops;
			result.setPosFilters(expandPositionFilters(focus, context));
			
			// simplify when only 1 child (frequent):
			if (result.ChildCount == 1)
			{
				Selection child = result.getChild(0);
				if (child.MatchOptions == mops && child.occRange == null && child.getPosFilters() == null && (result.getPosFilters() == null || result.getPosFilters().content == MatchOptions.UNSPECIFIED))
				{
					// transfer filters to child:
					child.setPosFilters(result.getPosFilters());
					child.occRange = result.occRange;
					child.Weight = result.Weight;
					return child;
				}
			}
			//        // ensure window filter on a phrase: (but after reduction)
			//        if(result instanceof FullText.Phrase) {
			//            FullText.Phrase phr = (FullText.Phrase) result;
			//            if(phr.getPosFilters() == null) {
			//                phr.setPosFilters(new PosFilters());
			//                phr.getPosFilters().window = phr.getChildCount();
			//            }
			//        }
			return result;
		}
		
		private Selection expandTerms(string tokens, SelectionList result, TextTokenizer wordSieve, MatchOptions mops, float weight)
		{
			wordSieve.start(tokens);
			int token = wordSieve.nextToken();
			result.Weight = weight;
			result.MatchOptions = mops;
			for (; token != System.Qizx.Api.FullText.TextTokenizer_Fields.END; )
			{
				if (token == System.Qizx.Api.FullText.TextTokenizer_Fields.WORD)
				{
					char[] tokenChars = wordSieve.TokenChars;
					Selection tq = wordSieve.gotWildcard()?(Selection) new Wildcard(tokenChars):new SimpleWord(tokenChars);
					tq.Weight = result.Weight;
					tq.MatchOptions = mops;
					result.addChild(tq);
				}
				token = wordSieve.nextToken();
			}
			return result;
		}
	}
}